/**
 * @file Define the Dianping Service class
 * 
 * @author Steve Gao<gaofu@parllay.com>
 */
const URL       = require('url');
const async     = require('async');
const cheerio   = require('cheerio');
const request   = require('request');

const BaseService = require('./base-service').BaseService;
const {SystemServices} = require('../constants');
const utils            = require('../utils');

const {RedisError} = require('../errors');

const redisKey       = require('../redisKey');

/**
 * The DianpingService class
 * 
 * @class DianpingService
 * @extends {BaseService}
 */
class DianpingService extends BaseService {

    get qiniuService() {
        if (!this._qiniuService) {
            this._qiniuService = this.getSystemService(SystemServices.QINIU_SERVICE);
        }

        return this._qiniuService;
    }

    get wallService() {
        if (!this._wallService) {
            this._wallService = this.getSystemService(SystemServices.WALL_SERVICE);
        }

        return this._wallService;
    }

    get wechatMessageService() {
        if (!this._wechatMessageService) {
            this._wechatMessageService = this.getSystemService(SystemServices.WECHAT_MESSAGE_SERVICE);
        }

        return this._wechatMessageService;
    }

    // We should have a client user agent library
    get RequestHeaders() {
        return {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
            'Cookie': '_hc.v="\"d343462d-daa9-4230-8f38-e835a43fe144.1472206778\""; __utma=205923334.1173030332.1477380615.1477380615.1477380615.1; __utmz=205923334.1477380615.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); PHOENIX_ID=0a0309b7-157fed84d16-42ba00; s_ViewType=10; JSESSIONID=3A477043E44DDD1DEC6BCE2E7E0348E3; aburl=1; cy=2; cye=beijing'
        };
    }

    getDPAllReviewsURL(shopId, pageNo) {
        //return  `http://www.dianping.com/shop/${shopId}/review_all${shopId ? `?pageno=${pageNo}` : ''}`;
        return  `http://www.dianping.com/shop/${shopId}/review_more_pic${shopId ? `?pageno=${pageNo}` : ''}`;
    }

    getShopURL(shopId) {
        return `http://www.dianping.com/shop/${shopId}`;
    }

    get tasks() {
        if (!this._tasks) {
            this._tasks = new Map();
        }

        return this._tasks;
    }

    /**
     * Fetch shop reviews from Dianping
     * 
     * @name fetchDianpingReviewsData
     * @param {String} wallId - The wall id
     * @param {String} userId - The user id
     * @param {String} shopId - The shop id
     * @param {String} host - The current host
     * @param {Function} parentCallback - The callback function
     * 
     * @returns {void}
     */
    fetchDianpingReviewsData(wallId, userId, shopId, host, parentCallback) {
        var me = this;

        if (this.tasks.get(shopId)) { //Check if the task exists for one shop
            return parentCallback({
                success: false,
                error: `正在爬取${shopId}的数据，无需再次触发.`
            });
        }

        // Record the latest comment id as the check point
        this.acquireStorageClients({
            redis: true
        }, (clients) => {
            var redis = clients.redis;

            var dianpingJobKey = redisKey.dianping_job(shopId);
            async.waterfall([
                (callback) => {
                    redis.hgetall(dianpingJobKey, (error, result) => {
                        if (error) {
                            me.reportError(error, {
                                wall_id: wallId,
                                user_id: userId,
                                shop_id: shopId
                            });
                            me.tasks.delete(shopId);
                            return;
                        }

                        callback(null, result);
                    });
                }, (dianpingJob, callback) => {
                    if (dianpingJob) {
                        callback(null, dianpingJob.last_comment_id);
                    } else {
                        me.wallService.getPhotosByWallId(wallId, {
                            source: 'dianping'
                        }, (photos) => {
                            if (photos.length === 0) {
                               callback(null, -1);
                            } else {
                                callback(null, photos[0].asset_meta_data.comment_id);
                            }
                        });
                    }
                }, (lastCommentId) => {
                    me.beginToFetch(wallId, userId, shopId, host, lastCommentId, () => {
                        redis.hset(dianpingJobKey, 'last_comment_id', me.tasks.get(shopId).last_comment_id, (error, result) => {
                            me.releaseStorageClients(clients);
                        });
                    });
                }
            ]);
        }, (error) => {
            me.logger.error(`Error happened when fetching data from shop [${shopId}]`, error);
            me.reportError(utils.buildRedisError(error, {
                wall_id: wallId,
                user_id: userId,
                shop_id: shopId
            }));
            me.tasks.delete(shopId);
        });

        

        parentCallback({
            success: true,
            data: `已开始爬取点评店铺[${shopId}]进入墙[${wallId}], 请稍后检查墙中的数据.`
        });
    }

    beginToFetch(wallId, userId, shopId, lastCommentId, host, parentCallback) {
        var me = this;
        var now = new Date();
        var twoYearsAgo = now;
        twoYearsAgo.setFullYear(now.getFullYear() - 2);
        let fetchInfo = {
            shop_id: shopId,
            current_page: 1,
            page_count: 10,
            timer_id: -1,
            wall_id: wallId,
            user_id: userId,
            is_fetching: true,
            date_limitation: twoYearsAgo,
            last_comment_id: lastCommentId,
            host: host,
            first_time: false
        };
        this.logger.info(`Begin to fetch reviews for Dianping shop [shopId].`);
        this.tasks.set(shopId, fetchInfo);

        this.fetchOnePage(shopId, null, fetchInfo.last_comment_id, () => {
            parentCallback();
        });

        fetchInfo.timer_id = setInterval(() => {
            let fetchInfo = me.tasks.get(shopId);
            if (fetchInfo.is_fetching) {
                return;
            }
            fetchInfo.current_page ++;
            if (fetchInfo.current_page === fetchInfo.page_count) {
                clearInterval(fetchInfo.timer_id);
            }

            me.fetchOnePage(shopId, fetchInfo.current_page, fetchInfo.last_comment_id);
        }, 20000);
    }

    fetchOnePage(shopId, pageNo, lastCommentId, parentCallback) {
        var me = this;
        var url = this.getDPAllReviewsURL(shopId, pageNo);

        this.tasks.get(shopId).is_fetching = true;

        var options = {
            method: 'get',
            headers: this.RequestHeaders,
            url: url
        };
        this.logger.info(`Fetching reviews from page ${pageNo ? pageNo : 1} ...`);
        request(options, (error, response, body) => {
            if (response.statusCode === 200) {
                var result = me.parsePageData(body, shopId, me.tasks.get(shopId).wall_id, pageNo, lastCommentId);
                if (!pageNo) {
                    parentCallback();
                }
                var photos = result.photos;
                var can_continue = result.can_continue;
                async.waterfall([
                    (callback) => {
                        me.saveDataIntoQiniu(photos, shopId, (photos) => {
                            callback(null, photos);
                        });
                    }, (photos, callback) => {
                        me.saveDataIntoWall(photos, (photos) => {
                            callback(null, photos);
                        });
                    }, (photos) => {
                        me.tasks.get(shopId).total_count += photos.length;
                        me.tasks.get(shopId).is_fetching = false;
                        me.logger.info(`Finished fetching reviews from page ${pageNo ? pageNo : 1}.`);
                        if (!can_continue) {
                            me.tasks.get(shopId).page_count = me.tasks.get(shopId).current_page;
                        }
                        if (me.tasks.get(shopId).current_page === me.tasks.get(shopId).page_count) {

                            if (me.tasks.get(shopId).total_count > 0) {
                                var articles = [];
                                articles.push({
                                    "title" : `刚刚为你爬取了${me.tasks.get(shopId).total_count}张大众点评照片，点击图片去审核吧`,
                                    "description" : '用户没有添加描述信息',
                                    "url" : `http://${me.tasks.get(shopId).host}/wall/${me.tasks.get(shopId).wall_id}#auditing/pending`,
                                    "picurl" : me.tasks.get(shopId).cover_image
                                });
                                me.wechatMessageService.sendCustomerServiceMessageToWallAdministrators(wallId, articles);
                            }
                            //TODO: Send customer message to the wall administrators
                            me.logger.info(`Finished fetching reviews for Dianping shop [shopId].`)
                            me.tasks.delete(shopId);
                        }
                    }
                ]);
            } else {
                this.tasks.get(shopId).is_fetching = false;
            }
        });
    }

    /**
     * Try to save all Dianping photos into Qiniu
     */
    saveDataIntoQiniu(photos, shopId, parentCallback) {
        var me = this;
        var functionChain = [];
        photos.forEach((photo) => {
            functionChain.push((callback) => {
                var urlObject = URL.parse(photo.photo_url);
                var fileKey = `dianping${urlObject.path}`;
                me.qiniuService.fetchURL(photo.photo_url, fileKey, (result) => {
                    if (!result || result && !result.key) {
                        photo.is_available = false;
                    }

                    if (result.mimeType) {
                        if (result.mimeType.startsWith("image/")) {
                            photo.asset_type = 'photo';
                        }

                        if (result.mimeType.startsWith("video/")) {
                            photo.asset_type = 'video';
                        }
                        photo.asset_meta_data.shop_id = shopId;
                        photo.asset_meta_data.mime_type = result.mimeType;
                    } else {
                        photo.asset_type = 'photo';
                    }

                    callback(null);
                });
            });

            functionChain.push((callback) => {
                var urlObject = URL.parse(photo.user.profile_image);
                if (photo.user.profile_image.indexOf("userheadpic") >= 0 || photo.user.profile_image.indexOf('relationwxpic') >= 0) {//TODO: Try to fech these kind of data into qiniu
                    photo.is_available = false;
                    photo.user.profile_image = me.getCDNImageURL('dashboard/images/default_profile_image.png');
                    callback(null);
                } else {
                    var fileKey = `dianping${urlObject.path}`
                    me.qiniuService.fetchURL(photo.user.profile_image, fileKey, (result) => {
                        if (!result || result && !result.key) {
                            photo.is_available = false;
                        }
                        photo.user.profile_image = me.getCDNImageURL(fileKey);
                        callback(null);
                    });
                }
            });

            functionChain.push((callback) => {
                var urlObject = URL.parse(photo.photo_url);
                var fileKey = `dianping${urlObject.path}`;
                me.qiniuService.getImageInfo(`http:${me.getCDNImageURL(fileKey)}`, (result) => {
                    if (result) {
                        photo.photo_url = me.getCDNImageURL(fileKey);
                        photo.asset_meta_data.width = result.width;
                        photo.asset_meta_data.height = result.height;
                        photo.asset_meta_data.ratio = Math.round(result.height / result.width * 100) / 100;
                        photo.ratio = photo.asset_meta_data.ratio;
                    }
                    callback(null);
                });
            });
        });

        functionChain.push(() => {
            parentCallback(photos);
        });

        async.waterfall(functionChain);
    }

    saveDataIntoWall(photos, parentCallback) {
        var me = this;
        var functionChain = [];
        photos.forEach((photo) => {
            if (photo.is_available) {
                functionChain.push((callback) => {
                    var user = photo.user;
                    me.wallService.saveOrUpdateWallUser(user, (userId) => {
                        callback(null, userId);
                    });
                });

                functionChain.push((userId, callback) => {
                    //delete photo.user;
                    me.wallService.savePhotoAssset(photo.wall_id, photo, {
                        id: userId,
                        name: photo.user.name,
                        profile_image: photo.user.profile_image
                    }, (result) => {
                        callback(null);
                    });
                });
            }
        });

        functionChain.push(() => {
            parentCallback(photos);
        });

        async.waterfall(functionChain);
    }

    parsePageData(html, shopId, wallId, pageNo, lastCommentId) {
        var me = this;
        var $ = cheerio.load(html);

        if (!pageNo) {
            var pageCount = Number($(".PageLink").last().attr('data-pg'));
            this.tasks.get(shopId).page_count = pageCount;
        }
        
        
        var photoSections = $('.shop-photo');
        var photoData = [];
        var can_continue = true;
        var isLastComment = false;
        photoSections.each(function(index) {
            var photoSection = $(this);
            var photoDescription = photoSection.siblings('.comment-txt').children('div').text();
            var userSection = $(this).parent().siblings('.pic');
            var userName = userSection.children('a').children('img').attr('title');
            var userId = userSection.children('a').attr('user-id');
            var profileImage = userSection.children('a').children('img').attr('src').replace('48c48', '120c120');
            var photos = photoSection.children('ul').children('li').children('a').children('img');
            var timeSection = photoSection.siblings('.misc-info');
            var commentId = photoSection.parent().parent().attr('data-id');
            var time = timeSection.children('.time').text();
            var spaceIndex = time.indexOf('更新');
            if (spaceIndex > 0) {
                time = time.substring(0, spaceIndex - 2);
            }
            var dTime = new Date(time);
            if (time.split('-').length === 2) {
                dTime.setFullYear(new Date().getFullYear());
            }

            if (!pageNo && index === 0) {
                me.tasks.get(shopId).last_comment_id = commentId;
            }

            if (!pageNo && lastCommentId === -1 && index === 0) {
                me.tasks.get(shopId).first_time = true;
            }

            if (dTime.getTime() >= me.tasks.get(shopId).date_limitation.getTime()) {
                
                photos.each(function(index, photo) {
                    //console.log($(photo).attr('panel-src'));
                    if (me.tasks.get(shopId).first_time || !me.tasks.get(shopId).first_time && commentId != me.tasks.get(shopId).last_comment_id && !isLastComment) {
                        photoData.push({
                            is_available: true,
                            wall_id: wallId,
                            photo_url: $(photo).attr('panel-src'),
                            description: photoDescription.trim(),
                            asset_meta_data: {
                                comment_id: commentId
                            },
                            user: {
                                open_id: 'dianping_' + userId,
                                wall_id: wallId,
                                name: userName,
                                profile_image: profileImage,
                                source: 'dianping',
                                is_anonymous: false
                            },
                            source: 'dianping',
                            status: 'pending',
                            created_date: dTime
                        });
                    } else {
                        can_continue = false;
                        if (!me.tasks.get(shopId).first_time) {
                            isLastComment = true;
                        }
                    }
                });
            } else {
                can_continue = false;
            }
        });

        return {
            can_continue: can_continue,
            photos: photoData
        };
    }

    fetchDianpingShopInfo(shopId, parentCallback) {
        let url = this.getShopURL(shopId);
        let options = {
            method: 'get',
            headers: this.RequestHeaders,
            url: url
        };

        request(options, (error, response, body) => {
            let $ = cheerio.load(body);

            let shopName = $("#basic-info").children('.shop-name').text();
            shopName = shopName && shopName.trim();
            let items = shopName && shopName.split("\n");
            shopName = items && items.length > 0 && items[0];
            let address = $("#basic-info").children('.address').children('.item').text();
            address = address && address.trim();
            let pnumbers = $("#basic-info").children('.tel').children('.item');
            let phone_numbers = [];
            pnumbers.each((index, number) => {
                phone_numbers.push($(number).text().trim());
            });
            let city = $("#page-header").children('.container').children('.J-city').text();
            let mainPhoto = $(".J_main-photo").children('img').attr('src');
            if (mainPhoto && mainPhoto.indexOf('meituan.net')) {
                mainPhoto = decodeURIComponent(mainPhoto);
                if (mainPhoto.indexOf('ugcpic') > 0) {
                    mainPhoto = mainPhoto.substring(0, mainPhoto.indexOf('@'));
                }
                if (mainPhoto.indexOf('dpmerchantalbum') > 0) {
                    mainPhoto = mainPhoto.substring(0, mainPhoto.indexOf('@'));
                }
            }

            parentCallback({
                id: shopId,
                name: shopName,
                address: address,
                phone_numbers: phone_numbers,
                city: city,
                logo: mainPhoto
            });

        });
    }
}

exports.DianpingService = DianpingService;